package com.googlecode.webduff.methods;

import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;
import org.dom4j.io.SAXReader;

import com.googlecode.webduff.WebdavStatus;
import com.googlecode.webduff.exceptions.MethodResponseError;
import com.googlecode.webduff.io.URI;
import com.googlecode.webduff.locking.LockFailedException;
import com.googlecode.webduff.locking.LockObject;
import com.googlecode.webduff.locking.ResourceLocks;

public class DoLock extends AbstractMethod {
	
	@SuppressWarnings("unused")
	private static org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(DoLock.class);
	
	public MethodResponse execute(HttpServletRequest request, HttpServletResponse resp) throws IOException, MethodResponseError {
		MethodResponse theResponse = new MethodResponse(resp);
		
		try {
			if(request.getHeader("if") != null) {
				doRefresh(request, resp, theResponse);
			} else {
				doLock(request, theResponse);
			}
		} catch (DocumentException e) {
			throw new MethodResponseError(WebdavStatus.SC_BAD_REQUEST);
		} catch (LockFailedException e) {
			throw new MethodResponseError(WebdavStatus.SC_LOCKED);
		}
		
		return theResponse;
	}

	private void doLock(HttpServletRequest request, MethodResponse response) throws DocumentException, IOException, LockFailedException {
		Document requestDocument = getRequestDocument(request);
		boolean isExclusive = false;
		
		if(requestDocument.selectSingleNode("/D:lockinfo/D:lockscope/D:exclusive") != null) {
			isExclusive = true;
		}
		
		if(requestDocument.selectSingleNode("/D:lockinfo/D:lockscope/D:shared") != null) {
			isExclusive = false;
		}
		
		int theDepth = 0;
		
		if(request.getHeader("depth") != null) {
			String definedDepth = request.getHeader("depth").trim();
			if(definedDepth.equals("infinity")) {
				theDepth = ResourceLocks.INFINITE;
			} else if(definedDepth.equals("0")) {
				theDepth = ResourceLocks.FLAT;
			}
		}
		
		URI theURI = getURIForRequest(request);
		
		LockObject aLock = null;
		
		if(isExclusive) {
			aLock = resourceLocks.exclusiveLock(theURI, "system.WebDuff", theDepth);
		} else {
			aLock = resourceLocks.sharedLock(theURI, "system.WebDuff", theDepth);
		}

		Document document = DocumentHelper.createDocument();
		document.setXMLEncoding("utf-8");
		
		Namespace namespace = new Namespace("D", "DAV:");
		
		Element root = document.addElement(QName.get("pro", namespace));
		
		root.add(namespace);
		
		Element activelock = root
			.addElement(QName.get("lockdiscovery", namespace))
			.addElement(QName.get("activelock", namespace));
		
		Element locktype = activelock.addElement(QName.get("locktype", namespace));
		
		if(isExclusive) {
			locktype.addElement(QName.get("write", namespace));
		} else {
			locktype.addElement(QName.get("read", namespace));
		}
		
		Element lockscope = activelock.addElement(QName.get("lockscope", namespace));
		
		if(isExclusive) {
			lockscope.addElement(QName.get("exclusive", namespace));
		} else {
			lockscope.addElement(QName.get("shared", namespace));
		}
		
		Element depth = activelock.addElement(QName.get("depth", namespace));
		
		if(theDepth == ResourceLocks.FLAT) {
			depth.setText("0");
		} else if(theDepth == ResourceLocks.INFINITE) {
			depth.setText("Infinity");
		}
		
		activelock
			.addElement(QName.get("owner", namespace))
			.addElement(QName.get("owner", namespace))
			.addElement(QName.get("href", namespace))
			.setText("system.WebDuff");
		
		activelock
			.addElement(QName.get("timeout", namespace))
			.setText("Second-84600");
		
		activelock
			.addElement(QName.get("locktoken", namespace))
			.addElement(QName.get("href", namespace))
			.setText("opaquelocktoken:" + aLock.getID());
		
		document.write(response.getWriter());
		
		response.setHeader("Lock-Token", "<opaquelocktoken:" + aLock.getID() + ">");
	}

	private void doRefresh(HttpServletRequest request, HttpServletResponse resp, MethodResponse response) throws IOException, MethodResponseError {
		String lockId = getLockIdFromRequest(request);
		if(lockId != null && resourceLocks.getLockByID(lockId) != null) {
			// send response
		} else {
			throw new MethodResponseError(WebdavStatus.SC_PRECONDITION_FAILED);
		}
	}

	private Document getRequestDocument(HttpServletRequest request) throws DocumentException, IOException {
		SAXReader theReader = new SAXReader();
		return theReader.read(request.getInputStream());
	}
	
}